package com.thinkgem.jeesite.common.utils;

import com.thinkgem.jeesite.common.config.Global;
import com.thinkgem.jeesite.common.persistence.Result;
import com.thinkgem.jeesite.modules.sys.entity.LicenseKey;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.sql.SQLException;
import java.util.Date;
import java.util.Random;

/**
 * RSA算法加密/解密工具类。
 *
 * @author tonglei
 * @version 1.0.0, 2015-05-14
 */
public class RSAUtils
{
    private static final Logger LOGGER = LoggerFactory.getLogger(RSAUtils.class);

    private static RSAPublicKey gongyao = null;
    private static RSAPrivateKey siyao = null;
    /**
     * 算法名称
     */
    private static final String ALGORITHOM = "RSA";

    /**
     * 密钥大小
     */
    private static final int KEY_SIZE = 1024;

    /**
     * 默认的安全服务提供者
     */
    private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();

    private static KeyPairGenerator keyPairGen = null;

    private static KeyFactory keyFactory = null;

    /**
     * 缓存的密钥对。
     */
    private static KeyPair oneKeyPair = null;

    static
    {
        try
        {
            keyPairGen = KeyPairGenerator.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
            keyFactory = KeyFactory.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
        }
        catch (NoSuchAlgorithmException ex)
        {
            LOGGER.error(ex.getMessage());
        }
    }

    private RSAUtils()
    {
    }

    /**
     * 生成并返回RSA密钥对。
     */
    private static KeyPair generateKeyPair()
    {
        try
        {
            keyPairGen.initialize(KEY_SIZE, new SecureRandom("asdafasdasdaf".getBytes()));
            oneKeyPair = keyPairGen.generateKeyPair();
            return oneKeyPair;
        }
        catch (InvalidParameterException ex)
        {
            LOGGER.error("KeyPairGenerator does not support a key length of " + KEY_SIZE + ".", ex);
        }
        catch (NullPointerException ex)
        {
            LOGGER.error("RSAUtils#KEY_PAIR_GEN is null, can not generate KeyPairGenerator instance.", ex);
        }
        return null;
    }

    /**
     * 返回RSA密钥对。
     */
    public static KeyPair getKeyPair()
    {
        // 直接强制生成密钥对文件，并存入数据库。
        return generateKeyPair();
    }

    /**
     * 根据给定的系数和专用指数构造一个RSA专用的公钥对象。
     *
     * @param modulus        系数。
     * @param publicExponent 专用指数。
     * @return RSA专用公钥对象。
     */
    public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent)
    {
        RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new BigInteger(modulus),
            new BigInteger(publicExponent));
        try
        {
            return (RSAPublicKey)keyFactory.generatePublic(publicKeySpec);
        }
        catch (InvalidKeySpecException ex)
        {
            LOGGER.error("RSAPublicKeySpec is unavailable.", ex);
        }
        catch (NullPointerException ex)
        {
            LOGGER.error("RSAUtils#KEY_FACTORY is null, can not generate KeyFactory instance.", ex);
        }
        return null;
    }

    /**
     * 根据给定的系数和专用指数构造一个RSA专用的私钥对象。
     *
     * @param modulus         系数。
     * @param privateExponent 专用指数。
     * @return RSA专用私钥对象。
     */
    protected static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent)
    {
        RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus),
            new BigInteger(privateExponent));
        try
        {
            return (RSAPrivateKey)keyFactory.generatePrivate(privateKeySpec);
        }
        catch (InvalidKeySpecException ex)
        {
            LOGGER.error("RSAPrivateKeySpec is unavailable.", ex);
        }
        catch (NullPointerException ex)
        {
            LOGGER.error("RSAUtils#KEY_FACTORY is null, can not generate KeyFactory instance.", ex);
        }
        return null;
    }

    /**
     * 根据给定的16进制系数和专用指数字符串构造一个RSA专用的私钥对象。
     *
     * @param hexModulus         系数。
     * @param hexPrivateExponent 专用指数。
     * @return RSA专用私钥对象。
     */
    protected static RSAPrivateKey getRSAPrivateKey(String hexModulus, String hexPrivateExponent)
    {
        if (StringUtils.isBlank(hexModulus) || StringUtils.isBlank(hexPrivateExponent))
        {
            LOGGER.error("hexModulus and hexPrivateExponent cannot be empty. RSAPrivateKey value is null to return.");
        }
        byte[] modulus = null;
        byte[] privateExponent = null;
        try
        {
            modulus = Hex.decodeHex(hexModulus.toCharArray());
            privateExponent = Hex.decodeHex(hexPrivateExponent.toCharArray());
        }
        catch (DecoderException ex)
        {
            LOGGER.error("hexModulus or hexPrivateExponent value is invalid. return null(RSAPrivateKey).");
        }
        if (modulus != null && privateExponent != null)
        {
            return generateRSAPrivateKey(modulus, privateExponent);
        }
        return null;
    }

    /**
     * 根据给定的16进制系数和专用指数字符串构造一个RSA专用的公钥对象。
     *
     * @param hexModulus        系数。
     * @param hexPublicExponent 专用指数。
     * @return RSA专用公钥对象。
     */
    public static RSAPublicKey getRSAPublidKey(String hexModulus, String hexPublicExponent)
    {
        if (StringUtils.isBlank(hexModulus) || StringUtils.isBlank(hexPublicExponent))
        {
            LOGGER.error("hexModulus and hexPublicExponent cannot be empty. return null(RSAPublicKey).");
        }
        byte[] modulus = null;
        byte[] publicExponent = null;
        try
        {
            modulus = Hex.decodeHex(hexModulus.toCharArray());
            publicExponent = Hex.decodeHex(hexPublicExponent.toCharArray());
        }
        catch (DecoderException ex)
        {
            LOGGER.error("hexModulus or hexPublicExponent value is invalid. return null(RSAPublicKey).");
        }
        if (modulus != null && publicExponent != null)
        {
            return generateRSAPublicKey(modulus, publicExponent);
        }
        return null;
    }

    /**
     * 使用指定的公钥加密数据。
     *
     * @param publicKey 给定的公钥。
     * @param data      要加密的数据。
     * @return 加密后的数据。
     */
    public static byte[] encrypt(PublicKey publicKey, byte[] data)
        throws Exception
    {
        Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
        ci.init(Cipher.ENCRYPT_MODE, publicKey);
        return ci.doFinal(data);
    }

    /**
     * 使用指定的私钥解密数据。
     *
     * @param privateKey 给定的私钥。
     * @param data       要解密的数据。
     * @return 原数据。
     */
    public static byte[] decrypt(PrivateKey privateKey, byte[] data)
        throws Exception
    {
        Cipher ci = Cipher.getInstance(ALGORITHOM, DEFAULT_PROVIDER);
        ci.init(Cipher.DECRYPT_MODE, privateKey);
        return ci.doFinal(data);
    }

    /**
     * 使用默认的公钥加密给定的字符串。
     * <p/>
     * 若{@code plaintext} 为 {@code null} 则返回 {@code null}。
     *
     * @param plaintext 字符串。
     * @return 给定字符串的密文。
     */
    public static String encryptString(String plaintext)
    {
        if (plaintext == null)
        {
            return null;
        }
        byte[] data = plaintext.getBytes();
        try
        {
            byte[] en_data = encrypt(gongyao, data);
            return new String(Hex.encodeHex(en_data));
        }
        catch (NullPointerException ex)
        {
            LOGGER.error("keyPair cannot be null.");
        }
        catch (Exception ex)
        {
            LOGGER.error(ex.getCause().getMessage());
        }
        return null;
    }

    /**
     * 使用给定的私钥解密给定的字符串。
     * <p/>
     * 若私钥为 {@code null}，或者 {@code encrypttext} 为 {@code null}或空字符串则返回 {@code null}。
     * 私钥不匹配时，返回 {@code null}。
     *
     * @param privateKey  给定的私钥。
     * @param encrypttext 密文。
     * @return 原文字符串。
     */
    public static String decryptString(PrivateKey privateKey, String encrypttext)
    {
        if (privateKey == null || StringUtils.isBlank(encrypttext))
        {
            return null;
        }
        try
        {
            byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
            byte[] data = RSAUtils.decrypt(privateKey, en_data);
            return new String(data);
        }
        catch (Exception ex)
        {
            LOGGER.error(String.format("\"%s\" Decryption failed. Cause: %s", encrypttext, ex.getCause().getMessage()));
        }
        return null;
    }

    /**
     * 使用默认的私钥解密给定的字符串。
     * <p/>
     * 若{@code encrypttext} 为 {@code null}或空字符串则返回 {@code null}。
     * 私钥不匹配时，返回 {@code null}。
     *
     * @param encrypttext 密文。
     * @return 原文字符串。
     */
    public static String decryptString(String encrypttext)
    {
        if (StringUtils.isBlank(encrypttext))
        {
            return null;
        }
        try
        {
            byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
            byte[] data = decrypt(siyao, en_data);
            return new String(data);
        }
        catch (NullPointerException ex)
        {
            LOGGER.error("keyPair cannot be null.");
        }
        catch (Exception ex)
        {
            LOGGER.error(String.format("\"%s\" Decryption failed. Cause: %s", encrypttext, ex.getMessage()));
        }
        return null;
    }

    /**
     * 返回已初始化的默认的私钥。
     *
     * @throws Exception
     * @throws SQLException
     */
    protected static RSAPrivateKey getDefaultPrivateKey()
        throws Exception
    {
        // 初始化私钥加密参数
        RSAPrivateCrtKeyParameters rsaPrivateCrtKeyParameters =
                new RSAPrivateCrtKeyParameters(new BigInteger("98512282862705199621660053246429064827027756292255053264614908624258661307811458648601076294029711018635745707137315511333738635761852937831411589435094460974617063371179077507387047420140476693428647678565621610929202184262110246885432983496072518478188574955587274815681905611293404451359785981401053928637"),
                        new BigInteger("65537"),
                        new BigInteger("27115416796013535803822660489682681545001353369180598834862571321131604915263300159636767250072508004107148890108641446952855810465966784343818509571992441025244742987825987316531786735318456011488871934908116473214203887708427613079061074230902473178257974686350177736282880192820836317209632195795014281985"),
                        new BigInteger("13364317235185837429218144168517750465112504808486860342334402861372612108144749208148584103115150455076842673132726693259637401462906304837959429356594369"),
                        new BigInteger("7371291860936982441528139508024150328027237592725821736435734762554055500163095123575314948760016935067655608770488413718361668472964703062373331647941373"),
                        new BigInteger("7223668671730825413012855929102229160262834281945138483098919495266237565328281672176868688965791685927675585593690745411437742182614293339333122137540609"),
                        new BigInteger("3853515273014662792456102471648311860300916843376709011886792326470001609641695561725649105993056444639714189115853693980572640242187522950699188482690989"),
                        new BigInteger("9881248390612262795004511275650620013111508825436352960484418357508075703368760223095395889504729224924170279471769233553377933041818266709359102635285786"));

        // 返回私钥对象
        return new LjRSAPrivateKey(rsaPrivateCrtKeyParameters);
    }

    public static String getRandomStringZM(int length) {
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    public static Result validateLicense(String license){
        try{
            siyao = RSAUtils.getDefaultPrivateKey();

            LicenseKey licenseKey = Global.getLicense();
            String tem = licenseKey.getLicenseId();

            if(license.length() < 756){
                return Result.error("注册文件中注册码长度错误！");
            }

            String jiemihou = RSAUtils.decryptString(siyao, license.substring(500, 756));
            String[] jiemihous = jiemihou.split("\\|");
            if(jiemihous.length == 3){
                boolean result = LOCALMAC.contains(tem);
                if(!result){
                    return Result.error("注册文件中MAC错误！");
                }
                result = tem.equals(jiemihous[0]);
                if(!result){
                    return Result.error("注册文件中注册码错误！");
                }

                Date beginDate = DateUtils.parseDate(jiemihous[1]);
                Date endDate = DateUtils.parseDate(jiemihous[2]);
                Date now = new Date();
                if(now.after(endDate) || now.before(beginDate)){
                    return Result.error("注册文件中有效日期错误！<br />日期：" + jiemihous[1] + " 到 " + jiemihous[2]);
                }
                else{
                    licenseKey.setLicenseKey(license);
                    licenseKey.setBegin(beginDate);
                    licenseKey.setEnd(endDate);
                    Global.setLicense(licenseKey);

                    return Result.success("注册成功！<br />日期：" + jiemihous[1] + " 到 " + jiemihous[2]);
                }
            }
        }
        catch(Exception e){
            e.printStackTrace();
        }
        return new Result(Result.FAILURE, "注册文件错误！", null);
    }
}
